package org.ielse.frame.bluetooth;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.ielse.frame.BaseService;
import org.ielse.frame.bluetooth.BleController.OnMessageReceivedListener;

import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.text.TextUtils;

/**
 * @author QQ517309507
 */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@SuppressWarnings("unused") 
public class BleService extends BaseService {

	public static final String ACTION_GATT_CONNECTED = "org.ielse.frame.bluetooth.ACTION_GATT_CONNECTED";
	public static final String ACTION_GATT_DISCONNECTED = "org.ielse.frame.bluetooth.ACTION_GATT_DISCONNECTED";
	public static final String EXTRA_DATA = "org.ielse.frame.bluetooth.EXTRA_DATA";

	private String currAddress;
	private BluetoothManager bluetoothManager;
	private BluetoothAdapter bluetoothAdapter;
	private final Map<String, BluetoothGatt> bluetoothGattMap = new HashMap<String, BluetoothGatt>();
	private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
		@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
			logd("onConnectionStateChange() status:" + status + ",newState:" + newState);
			if (newState == BluetoothProfile.STATE_CONNECTED) {
				gatt.discoverServices();
				setCurrAddress(gatt.getDevice().getAddress());
				broadcast(ACTION_GATT_CONNECTED, gatt.getDevice().getAddress());
			}
			else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
				setCurrAddress("");
				broadcast(ACTION_GATT_DISCONNECTED, gatt.getDevice().getAddress());
			}
		}

		@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) {
			logd("onServicesDiscovered() status:" + status);
			initGattProperties();
		}

		@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
			logd("onCharacteristicChanged()");
			byte[] value = characteristic.getValue();
			logd("characteristic value:" + value != null ? BleController.bytesToHex(value) : "empty");

			bleController.dealRingMessage(characteristic.getValue());
		}

		@Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
			logd("onCharacteristicRead() status:" + status);
			byte[] value = characteristic.getValue();
			logd("characteristic value:" + value != null ? BleController.bytesToHex(value) : "empty");
		}

		@Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
			logd("onCharacteristicWrite() status:" + status);
			byte[] value = characteristic.getValue();
			logd("characteristic value:" + value != null ? BleController.bytesToHex(value) : "empty");
		};
	};

	private void broadcast(String action, Object... obj) {
		final Intent intent = new Intent(action);
		intent.putExtra(EXTRA_DATA, obj);
		sendBroadcast(intent);
	}

	@Override public void onCreate() {
		super.onCreate();
		if (!init()) {
			stopSelf();
			return;
		}
		initServiceProperties();
	}

	private void initServiceProperties() {
	}

	private void initGattProperties() {
	}

	private void restoreGattProperties() {

	}

	@Override public void onDestroy() {
		super.onDestroy();
		for (String key : bluetoothGattMap.keySet()) {
			BluetoothGatt gatt = bluetoothGattMap.get(key);
			if (gatt != null) {
				gatt.disconnect();
				gatt.close();
				gatt = null;
			}
		}
		bluetoothGattMap.clear();
	}

	private boolean init() {
		if (bluetoothManager == null) {
			bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
			if (bluetoothManager == null) {
				loge("init() bluetoothManager fail");
				return false;
			}
		}

		bluetoothAdapter = bluetoothManager.getAdapter();
		if (bluetoothAdapter == null) {
			loge("init() bluetoothAdapter fail");
			return false;
		}
		logi("init() success");
		return true;
	}

	public void stopIfDisconnect() {
		if (bluetoothGattMap.size() < 1) {
			stopSelf();
		}
	}

	public boolean isEnable() {
		return bluetoothAdapter.isEnabled();
	}

	public void startLeScan(BluetoothAdapter.LeScanCallback callback) {
		loge("startLeScan() callback:" + callback);
		if (callback != null) {
			bluetoothAdapter.startLeScan(callback);
		}
	}

	public void stopLeScan(BluetoothAdapter.LeScanCallback callback) {
		loge("startLeScan() callback:" + callback);
		if (callback != null) {
			bluetoothAdapter.stopLeScan(callback);
		}
	}

	public void autoConnectLastAddress() {
	}

	public boolean connect(String address) {
		BluetoothGatt bluetoothGatt = getBluetoothGattByAddress(address);
		if (bluetoothGatt != null) {
			if (bluetoothGatt.connect()) {
				logi("connect() use an existing bluetoothGatt for connection. success");
				setCurrAddress(address);
				broadcast(ACTION_GATT_CONNECTED, address);
				return true;
			}
			else {
				loge("BleService connect() fail ?");
				// close ?
				disconnectAndClose(address);
			}
		}

		final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
		if (device == null) {
			loge("connect() device not found. unable to connect.");
			return false;
		}
		bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
		bluetoothGattMap.put(address, bluetoothGatt);
		logi("connect() use a new bluetoothatt for connection. success");

		return bluetoothGatt != null;
	}

	public void disconnectAndClose(String address) {
		BluetoothGatt bluetoothGatt = getBluetoothGattByAddress(address);
		if (bluetoothGatt != null) {
			bluetoothGatt.disconnect();
			bluetoothGatt.close();
			bluetoothGattMap.remove(address);
			bluetoothGatt = null;
		}
	}

	public void setCurrAddress(String address) {
		currAddress = address;
		// save to db ?
	}

	public String getCurrAddress() {
		return currAddress;
	}

	private BluetoothGatt getBluetoothGattByAddress(String address) {
		if (TextUtils.isEmpty(address)) {
			loge("getBluetoothGattByAddress() empty address ");
			return null;
		}
		return bluetoothGattMap.get(address);
	}

	private byte[] read(String address, UUID characteristicId) {
		final BluetoothGatt bluetoothGatt = getBluetoothGattByAddress(address);
		if (bluetoothGatt == null) {
			loge("read() empty bluetoothGatt ");
			return null;
		}
		final BluetoothGattService service = bluetoothGatt.getService(BleController.SERVICE);
		if (service == null) {
			loge("read() empty service ");
			return null;
		}
		final BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicId);
		if (characteristic == null) {
			loge("read() empty characteristic ");
			return null;
		}
		bluetoothGatt.readCharacteristic(characteristic);
		return characteristic.getValue();
	}

	private boolean write(String address, UUID characteristicId, byte[] value) {
		final BluetoothGatt bluetoothGatt = getBluetoothGattByAddress(address);
		if (bluetoothGatt == null) {
			loge("write() empty bluetoothGatt ");
			return false;
		}
		final BluetoothGattService service = bluetoothGatt.getService(BleController.SERVICE);
		if (service == null) {
			loge("write() empty service ");
			return false;
		}
		final BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicId);
		if (characteristic == null) {
			loge("write() empty characteristic ");
			return false;
		}

		characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
		characteristic.setValue(value);
		bluetoothGatt.writeCharacteristic(characteristic);
		return true;
	}

	public void request(String address) {
		logd("BleService request() " + address);
		byte[] value = BleController.VALUE_;
		write(address, BleController.CHARACTERISTIC_, value);
	}

	/**
	 * Enables or disables notification on a give characteristic.
	 * 
	 * @param address
	 *            device address
	 * @param characteristicId
	 *            Characteristic to act on.
	 * @param enabled
	 *            If true, enable notification. False otherwise.
	 */
	public boolean setCharacteristicNotification(String address, UUID characteristicId, boolean enabled) {
		final BluetoothGatt bluetoothGatt = getBluetoothGattByAddress(address);
		if (bluetoothGatt == null) {
			loge("setCharacteristicNotification() empty bluetoothGatt ");
			return false;
		}
		final BluetoothGattService service = bluetoothGatt.getService(BleController.SERVICE);
		if (service == null) {
			loge("setCharacteristicNotification() empty service ");
			return false;
		}
		final BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicId);
		if (characteristic == null) {
			loge("setCharacteristicNotification() empty characteristic ");
			return false;
		}
		bluetoothGatt.setCharacteristicNotification(characteristic, enabled);
		BluetoothGattDescriptor descriptor = characteristic.getDescriptor(BleController.CHARACTERISTIC_CLIENT_NOTIFICATION);
		descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
		bluetoothGatt.writeDescriptor(descriptor);
		return true;
	}

	private final BleController bleController = new BleController(new OnMessageReceivedListener() {
		@Override public void logic() {

		}
	});

}
